/** @file         led_drv.c
 *  @brief        驱动。
 *  @details      led 驱动内容。主要是 file_operations。本驱动为 chip for NXP IMX6 专用。
 *  @author       lzm
 *  @date         2021-03-06 10:19:03
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/

#include "led_drv.h"




/* led device */
static led_dev_t led_dev_elem[LED_DEV_CNT]; // 创建 LED_DEV_CNT 个 led device elem



/* [drv][1] */
/** @brief  led_init
  * @param  id: 设备内部id.
  * @retval 
  * @author lzm
  */
static void led_init(unsigned char id)
{
    unsigned int val = 0;
    led_pin_t *pin = led_dev_elem[id].pin_data; // 获取设备引脚数据

    /* MMU 地址映射 */
    pin->va_dr = ioremap(pin->resource->pa_dr, 4);
	pin->va_gdir = ioremap(pin->resource->pa_gdir, 4);
	pin->va_iomuxc_mux = ioremap(pin->resource->pa_iomuxc_mux, 4);
	pin->va_ccm_ccgrx = ioremap(pin->resource->pa_ccm_ccgrx, 4);
	pin->va_iomux_pad = ioremap(pin->resource->pa_iomux_pad, 4);

    /* 初始化时钟 */
    val = ioread32(pin->va_ccm_ccgrx);
    val &= ~(3 << pin->clock_offset);
    val |= (3 << pin->clock_offset);
    iowrite32(val, pin->va_ccm_ccgrx);
    /* 端口复用 */
    iowrite32(0x05, pin->va_iomuxc_mux);
    /* 电气属性 */
    iowrite32(0x0001F838, pin->va_iomux_pad);
    /* 输出模式 */
	val = ioread32(pin->va_gdir);
	val &= ~(1 << pin->pin);
	val |= (1 << pin->pin);
	iowrite32(val, pin->va_gdir);
    /* 输出高电平 */
	val = ioread32(pin->va_dr);
	val |= (0x01 << pin->pin);
	iowrite32(val, pin->va_dr);
}

/* [drv][2] */
/** @brief  led_exit
  * @param  id: 设备内部id.
  * @retval 
  * @author lzm
  */
static void led_exit(unsigned char id)
{
    led_pin_t *pin = led_dev_elem[id].pin_data; // 获取设备引脚数据

    /* 取消映射 */
    iounmap(pin->va_dr);
	iounmap(pin->va_gdir);
	iounmap(pin->va_iomuxc_mux);
	iounmap(pin->va_ccm_ccgrx);
	iounmap(pin->va_iomux_pad);
}

/* [drv][3] */
/** @brief  led_ctrl
  * @param  
  * @retval 
  * @author lzm
  */
static void led_ctrl(unsigned char id, unsigned char on_off)
{
    unsigned long val = 0;

    led_pin_t *pin = led_dev_elem[id].pin_data; // 获取设备引脚数据

    val = ioread32(pin->va_dr);
    if(!on_off)
    {
        val &= ~(0x01 << pin->pin);
        led_dev_elem[id].status = 0;
    }
    else
    {
        val |= (0x01 << pin->pin);
        led_dev_elem[id].status = 1;
    }
    
    iowrite32(val, pin->va_dr);
}

/* [drv][4] */
/** @brief   led_dev_init
  * @details led 设备元素表基本初始化。插入模块使用。
  * @param  
  * @retval 
  * @author lzm
  */
void led_dev_init(void)
{
    unsigned char i;

    for(i=0; i<LED_DEV_CNT; i++)
    {
        led_dev_elem[i].id = i;
        led_dev_elem[i].status = 0; // 状态默认关闭
        led_dev_elem[i].init = led_init;
        led_dev_elem[i].exit = led_exit;
        led_dev_elem[i].ctrl = led_ctrl;
        led_dev_elem[i].pin_data->resource = get_led_resource(i + '0'); // 获取 led 物理资源
    }

    // 命名
    strcpy(led_dev_elem[0].name, "led red");
    strcpy(led_dev_elem[1].name, "led green");
    strcpy(led_dev_elem[2].name, "led blue");
}

/* [sys][1][1] */
/** @brief  led_dev_open
  * @param 
  * @retval 
  * @author lzm
  */
int led_dev_open(struct inode *inode, struct file *filp)
{
    unsigned char i;
    unsigned char midev_num; // 次设备号

    midev_num = MINOR(inode->i_rdev);
    if(midev_num > LED_DEV_CNT) // 检查次设备号
        return -1;

    printk("led dev open!\n");
    printk("device num is [%d]\n", inode->i_rdev); // 写其它驱动时可以根据不同的设备号作不同初始化

    if(midev_num == 0) // 打开所有led
    {
        for(i=0; i<LED_DEV_CNT; i++)
        {
            if(led_dev_elem[i].pin_data->resource == 0)
                return -1;  
            led_dev_elem[i].init(i); // 初始化引脚
        }
    }
    else
    {
        if(led_dev_elem[midev_num].pin_data->resource == 0)
            return -1;
        led_dev_elem[midev_num-1].init(midev_num-1); // 初始化引脚 
    }

    return 0;
}

/* [sys][1][2] */
/** @brief  led_dev_release
  * @param 
  * @retval 
  * @author lzm
  */
int led_dev_release(struct inode *inode, struct file *filp)
{
    unsigned char i;
    unsigned char midev_num; // 次设备号

    midev_num = MINOR(inode->i_rdev);
    if(midev_num > LED_DEV_CNT) // 检查次设备号
        return -1;

    printk("led dev exit!\n");

    if(midev_num == 0) // 退出所有设备
    {
        for(i=0; i<LED_DEV_CNT; i++)
        {
            led_dev_elem[i].exit(i); // 出口函数
        }
    }
    else
    {
        led_dev_elem[midev_num-1].exit(midev_num-1); // 出口函数
    }

    return 0;
}

/* [sys][1][3] */
/** @brief  led_dev_write
  * @param 
  * @retval 
  * @author lzm
  */
ssize_t led_dev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos)
{
    unsigned char i;
    unsigned char midev_num; // 次设备号
    int ret;
    char ret_buf[2] = {LED_DEV_CNT+1};

    midev_num = MINOR(filp->f_inode->i_rdev);
    if(midev_num > LED_DEV_CNT) // 检查次设备号
        return -1;
    
    ret = copy_from_user(ret_buf, buf, 2); // 前为设备，后为开关
    if(ret_buf[0] > LED_DEV_CNT)
        return 0;

    if(ret_buf[0] == 0) // 打开所有
    {
        for(i=0; i<LED_DEV_CNT; i++)
        {
            led_dev_elem[i].ctrl(led_dev_elem[i].id, ret_buf[1]);
        }
    }
    else
    {
        led_dev_elem[ret_buf[0]-1].ctrl(led_dev_elem[ret_buf[0]-1].id, ret_buf[1]);
    }

    return 2;
}


/* [sys][1][4] */
/** @brief  led_dev_read
  * @param 
  * @retval 
  * @author lzm
  */
ssize_t led_dev_read(struct file *filp, char __user * buf, size_t count, loff_t *ppos)
{
    unsigned char i;
    unsigned char midev_num; // 次设备号
    int ret;
    char ret_buf[3] = {LED_DEV_CNT+1};

    midev_num = MINOR(filp->f_inode->i_rdev);
    if(midev_num > LED_DEV_CNT) // 检查次设备号
        return -1;

    if(midev_num == 0) // 打开所有
    {
        for(i=0; i<LED_DEV_CNT; i++)
        {
            ret_buf[i] = led_dev_elem[i].status;
        }
        ret = copy_to_user(buf, ret_buf, 3);
        return 3;
    }
    else
    {
        ret_buf[0] = led_dev_elem[midev_num-1].status;
        ret = copy_to_user(buf, ret_buf, 1);
        return 1;
    }
}


